package com.kzj.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.kzj.dao.AcademyDao;
import com.kzj.domain.*;
import com.kzj.dao.TeacherDao;
import com.kzj.domain.dto.ChangePasswordDTO;
import com.kzj.domain.dto.InputScoreDTO;
import com.kzj.domain.dto.UserLoginDTO;
import com.kzj.domain.vo.*;
import com.kzj.global.constant.GlobalConstant;
import com.kzj.global.exception.MyException;
import com.kzj.global.re.Result;
import com.kzj.global.threadLocal.UserThreadLocal;
import com.kzj.global.utils.EncodeUtil;
import com.kzj.global.utils.GlobalUtil;
import com.kzj.global.utils.IdGeneratorUtil;
import com.kzj.global.utils.MatchesUtil;
import com.kzj.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.swagger.models.auth.In;
import jodd.util.StringUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author kzj
 * @since 2023-12-01
 */
@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherDao, Teacher> implements TeacherService {
    @Autowired
    TeacherDao teacherDao;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    AcademyDao academyDao;
    @Autowired
    CourseInformationService courseInformationService;
    @Autowired
    EletiveCourseService eletiveCourseService;
    @Autowired
    MajorService majorService;
    @Autowired
    StudentService studentService;
    @Autowired
    ClassroomService classroomService;
    @Autowired
    InvigilatorInformationService invigilatorInformationService;
    @Autowired
    ExaminationService examinationService;
    @Autowired
    ScoreService scoreService;
    @Autowired
    CourseSelectionInformationService courseSelectionInformationService;
    @Autowired
    StudentClassService studentClassService;
    @Autowired
    CourseRestrictedMajorsService courseRestrictedMajorsService;

//    @Autowired
//    ExaminationService examinationService;
    @Override
    public Result login(UserLoginDTO userLoginDTO) {

        LambdaQueryWrapper<Teacher> lqw=new LambdaQueryWrapper<>();
        lqw.eq(Teacher::getId,userLoginDTO.getId())
                .eq(Teacher::getPassword,userLoginDTO.getPassword());
        Teacher teacher = teacherDao.selectOne(lqw);
        if(teacher==null)
             throw new MyException(GlobalConstant.ID_OR_PASSWORD_ERROR);
        if(teacher.getStatus().equals(GlobalConstant.STATUS_UNUSABLE))
            throw new MyException(GlobalConstant.ACCOUNT_DISABLED);
        teacher.setPassword(null);
        TeacherVO teacherVO=getTeacherVO(teacher);
        String key= EncodeUtil.encode(teacher.getId(),GlobalConstant.TEACHER_INFO_PREFIX);
        UserLoginVO userLoginVO = new UserLoginVO();
        userLoginVO.setObj(teacherVO);
        userLoginVO.setType(GlobalConstant.TEACHER_TYPE);
        userLoginVO.setToken(key);
        stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(userLoginVO),
                GlobalConstant.LOGIN_INFO_TTL, TimeUnit.MINUTES);
        return Result.success(userLoginVO);
    }
    @Override
    public Result update(Teacher teacher) {
        MatchesUtil.idMatches(teacher.getId(),Teacher.class);
        MatchesUtil.objMatches(teacher);
        teacher.setPassword(null);
        teacherDao.updateById(teacher);
        String key=EncodeUtil.encode(teacher.getId(),GlobalConstant.TEACHER_INFO_PREFIX);
        stringRedisTemplate.delete(key);
        return Result.success();
    }
    @Override
    public Result addTeacher(Teacher teacher) {
        MatchesUtil.objMatches(teacher);
        String id = IdGeneratorUtil.generatorId(Teacher.class);
        teacher.setId(id);
        if(teacher.getPassword()==null)
        {
            String idNumber = teacher.getIdNumber();
            String password=idNumber.substring(idNumber.length()-GlobalConstant.PASSWORD_DEFAULT_LEN);
            teacher.setPassword(EncodeUtil.encode(password));
        }
        else teacher.setPassword(EncodeUtil.encode(teacher.getPassword()));
        teacher.setStatus(GlobalConstant.STATUS_UNUSABLE);
        this.save(teacher);
        return Result.success();
    }

    @Override
    public Result changePassword(ChangePasswordDTO changePasswordDTO) {
        MatchesUtil.passwordMatchers(changePasswordDTO.getNewPassword());
        MatchesUtil.passwordMatchers(changePasswordDTO.getOldPassword());
        TeacherVO user = UserThreadLocal.getUser(TeacherVO.class);
        String password = this.query().le("id", user.getId()).one().getPassword();
        if(!password.equals(EncodeUtil.encode(changePasswordDTO.getOldPassword())))
        {
            throw new MyException(GlobalConstant.PASSWORD_ERROR);
        }
        String encodeNewPassword=EncodeUtil.encode(changePasswordDTO.getNewPassword());
        this.update().eq("id",user.getId()).set("password",encodeNewPassword).update();
        return Result.success();
    }

    @Override
    public Result getTeacherById(String id) {
        Teacher teacher = teacherDao.selectById(id);
        return Result.success(getTeacherVO(teacher));
    }

    @Override
    public Result getAllTeacherByAcademyId(String academyId) {
        List<Teacher> teachers = this.query().eq("academy_id", academyId).list();
        List<TeacherVO> collect = teachers.stream().map(this::getTeacherVO).collect(toList());
        return Result.success(collect);
    }

    @Override
    public Result getCourseList() {
        List<CourseInformation> courseInformations=(List<CourseInformation>)courseInformationService.getAllCourseInformation().getData();
        List<OptionalCourseVO> list=new ArrayList<>(courseInformations.size());
        for (int i = 0; i < courseInformations.size(); i++) {
            CourseInformation courseInformation=courseInformations.get(i);
            String major="通选课";
            String restrictedMajorIds=courseRestrictedMajorsService.getRestrictedMajorsByCourseId(courseInformation.getId());
            if(!restrictedMajorIds.equals(GlobalConstant.COURSE_RESTRICTED_MAJORS_GENERAL_ELECTIVE_COURSE))
            {
                List<Major> majors = majorService.query().in("id", restrictedMajorIds.split(",")).list();
                List<String> majorName = majors.stream().map(item -> item.getName()).collect(toList());
                major= StringUtil.join(majorName,",");
            }
            list.add(OptionalCourseVO.builder()
                            .id(""+(i+1))
                            .lesson(courseInformation.getName())
                            .number(courseInformation.getId())
                            .major(major)
                            .start("")
                            .end("")
                            .day("")
                            .time("")
                            .remainder(0)
                            .year("")
                            .term("")
                    .build());
        }
        return Result.success(list);
    }

    @Override
    public Result getArrangeCourseList() {
        Teacher user = UserThreadLocal.getUser(Teacher.class);
        return eletiveCourseService.getArrangeCourseList(user.getId(), GlobalUtil.getCurrOpeningDate(),null);
    }


    @Override
    public Result getManagerialStudent() {
        Teacher user = UserThreadLocal.getUser(Teacher.class);
        List<String> studentClasseIds = studentClassService.query().eq("counselor_id", user.getId()).list().stream().map(item->item.getId()).collect(toList());
        if(studentClasseIds.isEmpty())
        {
            return Result.error(GlobalConstant.NOT_MANAGE_CLASS);
        }
        List<Student> students = studentService.query().in("student_class_id", studentClasseIds).last("order by field(student_class_id," + GlobalUtil.getHandlerList(studentClasseIds) + ")").list();
        List<StudentVO> collect = students.stream().map(item -> studentService.getStudentVO(item)).collect(toList());
        return Result.success(collect);
    }

    @Override
    public Result getFreeClassroom(FreeClassroomDTO freeClassroomDTO) {
        return classroomService.getFreeClassroom(freeClassroomDTO);
    }

    @Override
    public Result getInvigilatorInformation(String teacherId) {

        return Result.success(getInvigilatorInformationByTeacherIdAndCurriculumTime(teacherId,GlobalUtil.getCurrentTerm()));
    }

    @Override
    public Result inputScore(InputScoreDTO inputScoreDTO) {
        Teacher user = UserThreadLocal.getUser(Teacher.class);
        Examination examination = examinationService.getById(inputScoreDTO.getExaminationId());
        if(examination==null)
            throw new MyException(GlobalConstant.EXAMINATION_NO_EXISTS);
        if(inputScoreDTO.getGrade()<0||inputScoreDTO.getGrade()>100)
            throw new MyException(GlobalConstant.PARAMS_SCORE_ERROR);


//        String[] split = examination.getStartTime().split(GlobalConstant.COURSE_SCHOOL_TIME_SPLIT);
//        LocalDateTime dateTime=GlobalUtil.getDateTimeByWeekAndDayAndTime(Integer.valueOf(split[1]),Integer.valueOf(split[0]),Integer.valueOf(split[2]));
//        if(LocalDateTime.now().isBefore(dateTime.minusMinutes(100)))
//            throw new MyException(GlobalConstant.EXAMINATION_NO_START);
//        if(LocalDateTime.now().isBefore(dateTime))
//            throw new MyException(GlobalConstant.EXAMINATION_NO_END);


        ElectiveCourse electiveCourse = eletiveCourseService.getById(examination.getElectiveCourseId());
        if(!electiveCourse.getTeacherId().equals(user.getId()))
        {
            throw  new MyException(GlobalConstant.TEACHER_PERMISSION_DENIED);
        }
        Score score= BeanUtil.copyProperties(inputScoreDTO, Score.class);
        score.setTeacherId(user.getId());
        score.setInputTime(LocalDateTime.now());
        Score one = scoreService.query().eq("examination_id", score.getExaminationId()).eq("student_id", score.getStudentId()).one();
        if(one==null)
        {
            scoreService.save(score);
            return Result.success(GlobalConstant.SCORE_INPUT_SUCCESS);
        }
        else{
            scoreService.update().eq("examination_id", score.getExaminationId()).eq("student_id", score.getStudentId()).set("grade",score.getGrade()).update();
            return Result.success(GlobalConstant.SCORE_UPDATE_SUCCESS);
        }
    }

    @Override
    public Result getStudentInformationThatCanBeEnteredIntoGrades() {
        Teacher user = UserThreadLocal.getUser(Teacher.class);
        List<ElectiveCourse> electiveCourses = eletiveCourseService.query().eq("teacher_id", user.getId()).eq("curriculum_time", GlobalUtil.getCurrentTerm()).list();
        List<ElectiveCourseVO> electiveCourseVOS= electiveCourses.stream().map(item -> eletiveCourseService.getElectiveCourseVO(item)).collect(toList());
        List<CourseScoreTeacherVO> courseScoreTeacherVOS =new ArrayList<>(electiveCourseVOS.size());
        for (ElectiveCourseVO electiveCourseVO : electiveCourseVOS) {
            CourseScoreTeacherVO courseScoreTeacherVO = new CourseScoreTeacherVO();
            courseScoreTeacherVO.setId(electiveCourseVO.getId());
            courseScoreTeacherVO.setLesson(electiveCourseVO.getCourseName());
            courseScoreTeacherVO.setType(electiveCourseVO.getType());
            courseScoreTeacherVO.setMag(Collections.emptyList());
            courseScoreTeacherVO.setMed(Collections.emptyList());
            List<StudentInfoByCourseVO> list=new ArrayList<>();
            List<String> studentIds = courseSelectionInformationService.getStudentIdsByElectiveCourseId(electiveCourseVO.getId());
            if(studentIds.isEmpty())
            {
                courseScoreTeacherVOS.add(courseScoreTeacherVO);
                continue;
            }
            List<Student> students = studentService.query().in("id", studentIds).last("order by field(id," + GlobalUtil.getHandlerList(studentIds) + ")").list();
            List<StudentInfoByCourseVO> studentInfoByCourseVOS = students.stream().map(item -> {
                        StudentVO studentVO = studentService.getStudentVO(item);
                        return  BeanUtil.copyProperties(studentVO, StudentInfoByCourseVO.class);
                    }
                    ).collect(toList());
            Examination examination = examinationService.query().eq("elective_course_id", electiveCourseVO.getId()).one();
            courseScoreTeacherVO.setExaminationId(examination.getId());
            List<Score> scores = scoreService.query().eq("examination_id",examination.getId()).in("student_id", studentIds).last("order by field(student_id," + GlobalUtil.getHandlerList(studentIds) + ")").list();
            if(scores.isEmpty())
            {
                courseScoreTeacherVO.setMag(studentInfoByCourseVOS);
                courseScoreTeacherVOS.add(courseScoreTeacherVO);
                continue;
            }
            List<StudentInfoByCourseVO> mag=new ArrayList<>();
            List<StudentInfoByCourseVO> med=new ArrayList<>();
            Iterator<Score> iterator = scores.iterator();
            Score score=iterator.next();
            for(int i=0;i<studentInfoByCourseVOS.size();i++)
            {
                if(studentInfoByCourseVOS.get(i).getId().equals(score.getStudentId()))
                {
                    studentInfoByCourseVOS.get(i).setGrade(score.getGrade());
                    med.add(studentInfoByCourseVOS.get(i));
                    if(iterator.hasNext())
                    {
                        score=iterator.next();
                    }
                    else{
                        if(i==studentInfoByCourseVOS.size()-1)
                            break;
                        mag.addAll(studentInfoByCourseVOS.subList(i+1,studentInfoByCourseVOS.size()));
                    }
                }
                else{
                    mag.add(studentInfoByCourseVOS.get(i));
                }
            }
            courseScoreTeacherVO.setMag(mag);
            courseScoreTeacherVO.setMed(med);
            courseScoreTeacherVOS.add(courseScoreTeacherVO);
        }
        return Result.success(courseScoreTeacherVOS);
    }

    @Override
    public Result addElectiveCourse(ElectiveCourse electiveCourse) {
        return eletiveCourseService.addCourse(electiveCourse);
    }

    @Override
    public Result getSchedule(String term) {
        Teacher user = UserThreadLocal.getUser(Teacher.class);
        return eletiveCourseService.getTeacherScheduleByTeacherIdAndTerm(user.getId(),term);
    }

    @Override
    public Result getCurrentTermSchedule() {
        Teacher user = UserThreadLocal.getUser(Teacher.class);
        return eletiveCourseService.getTeacherCurrentTermScheduleByTeacherId(user.getId());
    }

    @Override
    public Result getNextTermSchedule() {
        Teacher user = UserThreadLocal.getUser(Teacher.class);
        return eletiveCourseService.getTeacherNextTermScheduleByTeacherId(user.getId());
    }

    @Override
    public Result getCanDeleteElectiveCourseList() {
        Teacher user = UserThreadLocal.getUser(Teacher.class);
       return getCanDeleteElectiveCourseList(user.getId());
    }
    @Override
    public Result getCanDeleteElectiveCourseList(String teacherId) {
        List<ElectiveCourse> electiveCourses = eletiveCourseService.query().eq("teacher_id", teacherId).list();
        electiveCourses=electiveCourses.stream().filter(new Predicate<ElectiveCourse>() {
            @Override
            public boolean test(ElectiveCourse electiveCourse) {
                List<CourseSelectionInformation> courseSelectionInformations = courseSelectionInformationService.query().eq("elective_course_id", electiveCourse.getId()).list();
                if(courseSelectionInformations.isEmpty())
                    return true;
                return false;
            }
        }).collect(toList());
        List<ElectiveCourseVO> collect=electiveCourses.stream().map(item->eletiveCourseService.getElectiveCourseVO(item)).collect(toList());
        String[] dayOfWeekList=GlobalConstant.DAY_OF_WEEK_LIST;
        String[] timeOfDayList=GlobalConstant.TIME_OF_DAY_LIST;
        List<OptionalCourseVO> list=new ArrayList<>();

        for (int i = 0; i < collect.size(); i++) {
            ElectiveCourseVO electiveCourseVO=collect.get(i);
            String[] split1 = electiveCourseVO.getCurriculumTime().split(GlobalConstant.CURRICULUM_TIME_SPLIT);
            String year=split1[0];
            String term=split1[1];
            String[] split = electiveCourseVO.getSchooltime().split(GlobalConstant.COURSE_SCHOOL_TIME_SPLIT);
            Integer dayOfWeek=Integer.valueOf(split[0]);
            Integer timeOfDay=Integer.valueOf(split[1]);
            list.add(OptionalCourseVO.builder()
                    .id((i+1)+"")
                    .lesson(electiveCourseVO.getCourseName())
                    .number(electiveCourseVO.getId())
                    .start(electiveCourseVO.getStartWeek().toString())
                    .end(electiveCourseVO.getEndWeek().toString())
                    .day(dayOfWeekList[dayOfWeek-1])
                    .time(timeOfDayList[timeOfDay-1])
                    .site(electiveCourseVO.getTotalAmount().toString())
                    .major(electiveCourseVO.getRestrictedMajorNames())
                    .year(year)
                    .term(term.equals(GlobalConstant.UPPER_TERM_FLAG)?GlobalConstant.UPPER_TERM:GlobalConstant.DOWN_TERM)
                    .build());
        }
        return Result.success(list);
    }

    @Override
    public Result updateTeacher(Teacher teacher) {
        MatchesUtil.idMatches(teacher.getId());
        MatchesUtil.objMatches(teacher);
        String key=EncodeUtil.encode(teacher.getId(),GlobalConstant.TEACHER_INFO_PREFIX);
        synchronized (teacher.getId())
        {
            this.update(teacher);
            Teacher s = this.getById(teacher.getId());
            UserLoginVO userLoginVO=new UserLoginVO();
            userLoginVO.setObj(s);
            userLoginVO.setType(GlobalConstant.TEACHER_TYPE);
            String jsonString = JSONObject.toJSONString(userLoginVO);
            stringRedisTemplate.opsForValue().set(key,jsonString,GlobalConstant.SCHEDULE_TTL,TimeUnit.MINUTES);
        }
        return Result.success();
    }

    public List<ExaminationVO> getInvigilatorInformationByTeacherIdAndCurriculumTime(String teacherId,String curriculumTime) {
        List<String> examinationIds= invigilatorInformationService.query().eq("teacher_id", teacherId).list()
                .stream().map(item->item.getExaminationId()).collect(toList());
        if(examinationIds.isEmpty())
            return Collections.emptyList();
        List<Examination> examinations = examinationService.query().in("id", examinationIds).list();
        List<String> classroomIds = examinations.stream().map(item -> item.getClassroomId()).collect(toList());
        List<Classroom> classrooms = classroomService.query().in("id", classroomIds).list();
        Map<String,String> map=new HashMap<>();
        for (Classroom classroom : classrooms) {
            map.put(classroom.getId(),classroom.getName());
        }
        List<String> electiveCourseIds = examinations.stream().map(item -> item.getElectiveCourseId()).collect(toList());
        List<ElectiveCourse> eletiveCourses = eletiveCourseService.query().in("id", electiveCourseIds).eq("curriculum_time", curriculumTime)
                .last("order by field(id,"+GlobalUtil.getHandlerList(electiveCourseIds)+")").list();
        List<ElectiveCourseVO> electiveCourseVOS = eletiveCourses.stream().map(item -> eletiveCourseService.getElectiveCourseVO(item)).collect(toList());
        Iterator<Examination> iterator = examinations.iterator();
        List<ExaminationVO> examinationVOS=new ArrayList<>();
        for (ElectiveCourseVO eletiveCoureVO : electiveCourseVOS) {
            Examination examination=iterator.next();

            while(!examination.getElectiveCourseId().equals(eletiveCoureVO.getId())&&iterator.hasNext())
            {
                examination=iterator.next();
            }
            ExaminationVO examinationVO=new ExaminationVO();
            String[] split = examination.getStartTime().split(GlobalConstant.COURSE_SCHOOL_TIME_SPLIT);
            examinationVO.setClassroomName(map.get(examination.getClassroomId()));
            examinationVO.setWeek(split[0]);
            examinationVO.setDayOfWeek(split[1]);
            examinationVO.setTimeOfDay(split[2]);
            examinationVO.setCourseName(eletiveCoureVO.getCourseName());
            examinationVOS.add(examinationVO);
        }
        return examinationVOS;
    }

    public TeacherVO getTeacherVO(Teacher teacher)
    {
        Academy academy = academyDao.selectById(teacher.getAcademyId());
        TeacherVO teacherVO = new TeacherVO();
        teacherVO.setAcademy(academy.getName());
        List<StudentClass> studentClasses = studentClassService.query().eq("counselor_id", teacher.getId()).list();
        List<String> manageClassList=new ArrayList<>();
        List<String> manageClassIds=new ArrayList<>();
        for (StudentClass studentClass : studentClasses) {
            manageClassList.add(studentClassService.getClassNameByStudentClass(studentClass));
            manageClassIds.add(studentClass.getId());
        }
        teacherVO.setManageClass(StringUtil.join(manageClassList,","));
        teacherVO.setManageClassIds(StringUtil.join(manageClassIds,","));
        BeanUtils.copyProperties(teacher,teacherVO);
        return teacherVO;
    }
}
